package com.hsc.dao;

import freemarker.template.Template;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MybatisGenerator {


    GeneratorProperties gp;
    private String automaticEntityPath;
    private String automaticDaoPath;
    private String automaticMapperPath;
    private String manualEntityPath;
    private String manualDaoPath;
    private String manualMapperPath;

    public MybatisGenerator(GeneratorProperties gp) throws Exception {
        this.gp = gp;

        String userDir = System.getProperty("user.dir")+"/";

        this.automaticEntityPath = userDir + gp.getEntityTargetProject() +"/"+ gp.getAutomaticEntityPackageName().replace(".", "/");
        this.manualEntityPath = userDir + gp.getEntityTargetProject() +"/"+  gp.getManualEntityPackageName().replace(".", "/");

        this.automaticDaoPath = userDir + gp.getDaoTargetProject() +"/"+  gp.getAutomaticDaoPackageName().replace(".", "/");
        this.manualDaoPath = userDir + gp.getDaoTargetProject() +"/"+  gp.getManualDaoPackageName().replace(".", "/");

        this.automaticMapperPath = userDir + gp.getMapperTargetProject() +"/"+  gp.getAutomaticMapperPackageName().replace(".", "/");
        this.manualMapperPath = userDir + gp.getMapperTargetProject() +"/"+  gp.getManualMapperPackageName().replace(".", "/");
    }

    public void generator() throws Exception {
        clearAutomaticMapper();
        createFile();
        editAutomaticEntityWithBLOBs();
        editAutomaticMapper();
        deleteAutomaticDao();
        createManualFile();
    }


    private void clearAutomaticMapper() throws Exception {
        List<File> fileList = getFiles(this.automaticMapperPath);
        for (File f : fileList) {
            String content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\r\n<mapper namespace=\"*\">\r\n</mapper>";
            writeFile(content, f, false);
        }
    }


    private void createFile() throws Exception {
        List<String> warnings = new ArrayList<>();
        //解析
        ConfigurationParser cp = new ConfigurationParser(warnings);
        File file = File.createTempFile("generator", ".xml");
        createTemplateFile(this.gp, "generatorConfig.ftl", file);
        Configuration config = cp.parseConfiguration(file);
        //是否覆盖
        DefaultShellCallback dsc = new DefaultShellCallback(true);
        MyBatisGenerator mg = new MyBatisGenerator(config, dsc, warnings);
        mg.generate(null);

    }


    private void editAutomaticEntityWithBLOBs() throws Exception {
        List<File> fileList = getFiles(this.automaticEntityPath);
        for (File file : fileList) {
            String fileName = file.getName();
            if (fileName.startsWith(gp.getAutomaticEntityPrefix()) && fileName.endsWith("WithBLOBs.java")) {
                String simpleEntityClassName = fileName.substring(gp.getAutomaticEntityPrefix().length(), fileName.lastIndexOf("WithBLOBs.java"));
                String content = readFile(file);
                content = content.replace("WithBLOBs extends " + gp.getAutomaticEntityPrefix(), "WithBLOBs extends ");
                content = content.replaceFirst(";", ";\r\n\r\nimport " + this.gp.getManualEntityPackageName() + "." + simpleEntityClassName + ";");
                writeFile(content, file, false);
            }
        }
    }

    private void editAutomaticMapper() throws Exception {

        List<File> fileList = getFiles(this.automaticMapperPath);
        for (File file : fileList) {
            String content = readFile(file);
            String simpleEntityClassName = file.getName().substring(gp.getAutomaticEntityPrefix().length(), file.getName().lastIndexOf("Mapper.xml"));
            content = content.replace(gp.getAutomaticEntityPrefix() + simpleEntityClassName, simpleEntityClassName);
            content = content.replace(this.gp.getAutomaticEntityPackageName(), this.gp.getManualEntityPackageName());
            content = content.replace(this.gp.getAutomaticDaoPackageName(), this.gp.getManualDaoPackageName());
            writeFile(content, file, false);
        }

    }

    private void deleteAutomaticDao() {
        File file = new File(this.automaticDaoPath);
        File[] files = file.listFiles();
        //遍历删除文件
        if (files != null) {
            for (File f : files) {
                f.delete();
            }
        }
        file.delete();
    }


    private void createManualFile() throws Exception {
        List<File> fileList = getFiles(this.automaticEntityPath);
        for (File file : fileList) {
            String fileName = file.getName();
            if (fileName.startsWith(gp.getAutomaticEntityPrefix()) && fileName.endsWith(".java")) {

                String simpleClassName = fileName.substring(gp.getAutomaticEntityPrefix().length(), fileName.lastIndexOf(".java"));

                if (!simpleClassName.endsWith("Example") && !simpleClassName.endsWith("WithBLOBs")) {
                    File withBLOBsFile = new File(this.automaticEntityPath + "/" + gp.getAutomaticEntityPrefix() + simpleClassName + "WithBLOBs.java");
                    createManualDao(simpleClassName, withBLOBsFile.exists());
                    createManualMapper(simpleClassName);
                }
                createManualEntity(simpleClassName);
            }
        }
    }


    private void createManualEntity(String simpleClassName) throws Exception {
        File f = new File(this.manualEntityPath + "/" + simpleClassName + ".java");
        if (!f.exists()) {
            f.getParentFile().mkdirs();
            Map temp = new HashMap();
            temp.put("package", this.gp.getManualEntityPackageName());
            temp.put("simpleEntityClassName", simpleClassName);
            temp.put("prefix", gp.getAutomaticEntityPrefix());
            temp.put("automaticEntityPackageName", this.gp.getAutomaticEntityPackageName());
            createTemplateFile(temp, "manualEntity.ftl", f);
        }

    }


    private void createManualDao(String simpleEntityClassName, boolean isBlobs) throws Exception {
        File f = new File(this.manualDaoPath + "/" + simpleEntityClassName + "Mapper.java");
        if (!f.exists()) {
            f.getParentFile().mkdirs();
            Map temp = new HashMap();
            temp.put("package", this.gp.getManualDaoPackageName());
            temp.put("manualEntityPackageName", this.gp.getManualEntityPackageName());
            temp.put("simpleEntityClassName", simpleEntityClassName);
            temp.put("isBlobs", isBlobs);
            createTemplateFile(temp, "manualDao.ftl", f);
        }
    }


    private void createManualMapper(String simpleEntityClassName) throws Exception {
        File f = new File(this.manualMapperPath + "/" + simpleEntityClassName + "Mapper.xml");
        if (!f.exists()) {
            f.getParentFile().mkdirs();
            Map temp = new HashMap();
            temp.put("manualDaoPackageName", this.gp.getManualDaoPackageName());
            temp.put("simpleEntityClassName", simpleEntityClassName);
            createTemplateFile(temp, "manualMapper.ftl", f);
        }
    }


    private String readFile(File file) throws Exception {
        return readFile(new FileInputStream(file));
    }

    private String readFile(InputStream inputStream) throws Exception {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String lineString;
        StringBuffer buffer = new StringBuffer();
        while ((lineString = bufferedReader.readLine()) != null) {
            buffer.append(lineString);
            buffer.append("\r\n");
        }
        String content = buffer.toString();
        bufferedReader.close();
        inputStream.close();
        return content;
    }


    private void writeFile(String content, File file, boolean isTrim) throws Exception {

        FileOutputStream fileOutputStream = new FileOutputStream(file);
        if (isTrim) {
            content = content.trim();
        }
        fileOutputStream.write(content.getBytes());
        fileOutputStream.flush();
        fileOutputStream.close();

    }

    List<File> getFiles(String path) {
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
        return doGetFiles(file);
    }

    /**
     * 读取某个目录下所有文件，如果含有文件夹，文件夹中的内容将会被递归读取。
     */
    private List<File> doGetFiles(File file) {

        List<File> files = new ArrayList<>();
        File[] tempList = file.listFiles();
        for (int i = 0; i < tempList.length; i++) {
            if (tempList[i].isFile()) {
                files.add(tempList[i]);
            }
            if (tempList[i].isDirectory()) {
                files.addAll(doGetFiles(tempList[i]));
            }
        }
        return files;
    }

    public void createTemplateFile(Object data, String templateName, File newFile) throws Exception {
        //设置配置类
        freemarker.template.Configuration configuration = new freemarker.template.Configuration();
        //设置模板所在的目录
        configuration.setClassForTemplateLoading(this.getClass(), "ftl");
        //设置字符集
        configuration.setDefaultEncoding("utf-8");
        //加载模板
        Template template = configuration.getTemplate(templateName);
        //创建Writer对象
        FileWriter writer = new FileWriter(newFile);
        //输出数据模型到文件中
        template.process(data, writer);
        //关闭Writer对象
        writer.flush();
        writer.close();
    }

}
